#include "swegl/renderer.h"
#include "swegl/profiling.h"
// memset
#include <string.h>
#include <cmath>
#include <math.h>

namespace swegl
{

VertexBuffer::VertexBuffer() :
	m_data(0), m_size(0)
{
}

VertexBuffer::~VertexBuffer()
{
	if (m_data) delete m_data;
}
	
void VertexBuffer::init(size_t size)
{
	m_data = new uint8[size];
	m_size = size;
}

//////

IndexBuffer::IndexBuffer() :
	m_data(0), m_numIndices(0), m_indexType(Index16Bit)
{
}

IndexBuffer::~IndexBuffer()
{
	if (m_data) delete m_data;
}
	
void IndexBuffer::init(IndexType indexType, size_t numIndices)
{
	m_data = new uint8[indexType == Index16Bit ? numIndices * 2 : numIndices * 4];
	m_numIndices = numIndices;
	m_indexType = indexType;
}

//////

Renderer::Renderer() :
	m_vertexBuffer(0),
	m_worldMatrix(Matrix4x4::Identity),
	m_viewMatrix(Matrix4x4::Identity),
	m_projectionMatrix(Matrix4x4::Identity),
	m_viewportScaleMatrix(Matrix4x4::Identity)
{
}

Renderer::~Renderer()
{
}

void Renderer::openWindow(uint width, uint height)
{
	sweglAssert(width > 0);
	sweglAssert(height > 0);

	m_rasterizer.m_colorBuffer = new Surface(width, height);
	m_rasterizer.m_depthBuffer = new Surface(width, height);

	m_transformedVertexArray.allocate(8 * 1024 * 1024);
	m_transformedIndexArray.reserve(1024 * 1024);

	m_nullTexture = new Texture(1, 1, 0);
	m_nullTexture->surface(0)->clear(0xff000000); // black

	for (size_t i = 0; i < 8; i++)
	{
		setTexture(i, 0);
	}

	setViewport(Viewport(0, 0, width, height));
}

void Renderer::closeWindow()
{
	// todo
}

void dump(const char *name, const Matrix4x4 &m)
{
	sweglInfo("matrix %s\n", name);
	for (uint i = 0; i < 4; i++)
	{
		sweglInfo("    %f %f %f %f\n", m[i].x, m[i].y, m[i].z, m[i].w);
	}
}

void dump(const char *name, const Vec3 &v)
{
	sweglInfo("vector %s: %f %f %f\n", name, v.x, v.y, v.z);
}

void dump(const char *name, const Vec4 &v)
{
	sweglInfo("vector %s: %f %f %f %f\n", name, v.x, v.y, v.z, v.w);
}

void Renderer::setViewport(const Viewport &viewport)
{
	sweglAssertMsg(viewport.x + viewport.width <= m_rasterizer.m_colorBuffer->width(), "%u %u %u", viewport.x, viewport.width, m_rasterizer.m_colorBuffer->width());
	sweglAssertMsg(viewport.y + viewport.height <= m_rasterizer.m_colorBuffer->height(), "%u %u %u", viewport.y, viewport.height, m_rasterizer.m_colorBuffer->height());

	m_viewport = viewport;
	m_clippingViewport = ClippingViewport(viewport);

	m_viewportScaleMatrix = Matrix4x4(
		static_cast<float>(viewport.width) / 2, 0, 0, 0,
		0, + static_cast<float>(viewport.height) / 2, 0, 0,
		0, 0, 1, 0,
		static_cast<float>(viewport.x) + static_cast<float>(viewport.width) / 2, static_cast<float>(viewport.y) + static_cast<float>(viewport.height) / 2, 0, 1
	);
}

void Renderer::clear(uint flags, uint32 color)
{
	if (flags & ClearFlags::ZBuffer)
	{
		m_rasterizer.m_depthBuffer->clear((float) HUGE_VAL);
	}
	if (flags & ClearFlags::Target)
	{
		m_rasterizer.m_colorBuffer->clear(color);
	}
}

void Renderer::setWorldMatrix(const Matrix4x4 &worldMatrix)
{
	m_worldMatrix = worldMatrix;
}

void Renderer::setViewMatrix(const Matrix4x4 &viewMatrix)
{
	m_viewMatrix = viewMatrix;
}

void Renderer::setProjectionMatrix(const Matrix4x4 &projectionMatrix)
{
	m_projectionMatrix = projectionMatrix;

	const Matrix4x4 &m = projectionMatrix;
	Plane nearPlane(
		m[0][3] + m[0][2],
		m[1][3] + m[1][2],
		m[2][3] + m[2][2],
		m[3][3] + m[3][2]);
	nearPlane.normalize();

	Plane farPlane(
		m[0][3] - m[0][2],
		m[1][3] - m[1][2],
		m[2][3] - m[2][2],
		m[3][3] - m[3][2]);
	farPlane.normalize();

	znear = nearPlane.distance();
	zfar = -farPlane.distance();
}

VertexBuffer *Renderer::createVertexBuffer(size_t size)
{
	VertexBuffer *vb = new VertexBuffer();
	vb->init(size);
	return vb;
}

IndexBuffer *Renderer::createIndexBuffer(IndexBuffer::IndexType indexType, size_t numIndices)
{
	IndexBuffer *ib = new IndexBuffer();
	ib->init(indexType, numIndices);
	return ib;
}

void Renderer::setTexture(uint unit, Texture *tex)
{
	m_rasterizer.m_textureUnits[unit].setTexture(tex ? tex : m_nullTexture);
}

void Renderer::setVertexBuffer(VertexBuffer *vertexBuffer)
{
	m_vertexBuffer = vertexBuffer;
}

void Renderer::setIndexBuffer(IndexBuffer *indexBuffer)
{
	m_indexBuffer = indexBuffer;
}

void Renderer::setVertexFormat(const InputVertexFormat &format)
{
	m_inputVertexFormat = format;
	
	m_rasterizer.vertexFormat = RenderVertexFormat(); // reinit structure
	if (m_inputVertexFormat.normalOffset >= 0)
	{
		m_rasterizer.vertexFormat.normalOffset = m_rasterizer.vertexFormat.numGradients;
		m_rasterizer.vertexFormat.numGradients += 3;
	}
	if (m_inputVertexFormat.diffuseOffset >= 0)
	{
		m_rasterizer.vertexFormat.diffuseOffset = m_rasterizer.vertexFormat.numGradients;
		m_rasterizer.vertexFormat.numGradients += 4;
	}
	if (m_inputVertexFormat.specularOffset >= 0)
	{
		m_rasterizer.vertexFormat.specularOffset = m_rasterizer.vertexFormat.numGradients;
		m_rasterizer.vertexFormat.numGradients += 4;
	}
	if (m_inputVertexFormat.textureOffset >= 0)
	{
		m_rasterizer.vertexFormat.textureOffset = m_rasterizer.vertexFormat.numGradients;
		m_rasterizer.vertexFormat.numGradients += m_inputVertexFormat.numTextureCoords * 2;
	}
	m_rasterizer.vertexFormat.vertexSize = sizeof(RenderVertex) + m_rasterizer.vertexFormat.numGradients * sizeof(float);

	m_transformedVertexArray.setVertexSize(m_rasterizer.vertexFormat.vertexSize);
}

uint8 Renderer::computeClipFlags(const RenderVertex *vertex)
{
	uint8 clipFlags = 0;
	clipFlags |= (vertex->viewspacePos.z <= znear) ? PlaneNear : 0;
	clipFlags |= (vertex->viewspacePos.z >= zfar) ? PlaneFar : 0;
	clipFlags |= (vertex->screenspacePos.x <= m_clippingViewport.left) ? PlaneLeft : 0;
	clipFlags |= (vertex->screenspacePos.x >= m_clippingViewport.right) ? PlaneRight : 0;
	clipFlags |= (vertex->screenspacePos.y <= m_clippingViewport.top) ? PlaneTop : 0;
	clipFlags |= (vertex->screenspacePos.y >= m_clippingViewport.bottom) ? PlaneBottom : 0;
	return clipFlags;
}

static inline Vec4 project(const Matrix4x4 &m, const Vec3 &p)
{
	Vec4 p1(p, 1);
	Vec4 p2 = m.transform(p1);
	p2 /= p2.w;
	return p2;
}

void Renderer::intersect(RenderVertex *v, const RenderVertex *v0, const RenderVertex *v1, float t)
{
	v->screenspacePos  = v0->screenspacePos  + t * (v1->screenspacePos  - v0->screenspacePos);
	v->viewspacePos    = v0->viewspacePos    + t * (v1->viewspacePos    - v0->viewspacePos);
	v->clipFlags = computeClipFlags(v);

	// skip fixed part of v0 and v1 to access gradients
	v++;
	v0++;
	v1++;
	float *clippedGradients = reinterpret_cast<float *>(v);
	const float *v0Gradients = reinterpret_cast<const float *>(v0);
	const float *v1Gradients = reinterpret_cast<const float *>(v1);
	for (size_t i = 0; i < m_rasterizer.vertexFormat.numGradients; i++)
	{
		clippedGradients[i] = v0Gradients[i] + t * (v1Gradients[i] - v0Gradients[i]);
	}
}

void Renderer::clipTriangleX(const IndexArray &originalVertices, IndexArray &clippedVertices, const ClipPlanes clipPlane, const float clipValue, RenderVertexArray &vertexArray)
{
	clippedVertices.clear();
	if (originalVertices.size() == 0) return;
	for (uint i = 0; i < originalVertices.size(); i++)
	{
		const uint index0 = originalVertices[i];
		const uint index1 = originalVertices[i == originalVertices.size() - 1 ? 0 : i + 1];

		const RenderVertex *v0 = vertexArray[index0];
		const RenderVertex *v1 = vertexArray[index1];

		if (v0->clipFlags & v1->clipFlags & clipPlane) // all outside
		{
		}
		else if (v0->clipFlags & clipPlane) // ouside to inside
		{
			clippedVertices.push_back(static_cast<uint32>(vertexArray.numVertices()));
			RenderVertex *v = vertexArray.insert();
			intersect(v, v0, v1, (clipValue - v0->screenspacePos.x) / (v1->screenspacePos.x - v0->screenspacePos.x));
			v->screenspacePos.x = clipValue; // adjust precision accuracy
		}
		else if (v1->clipFlags & clipPlane) // inside to outside
		{
			clippedVertices.push_back(index0);
			clippedVertices.push_back(static_cast<uint32>(vertexArray.numVertices()));
			RenderVertex *v = vertexArray.insert();
			intersect(v, v0, v1, (clipValue - v0->screenspacePos.x) / (v1->screenspacePos.x - v0->screenspacePos.x));
			v->screenspacePos.x = clipValue;
		}
		else
		{
			clippedVertices.push_back(index0);
		}
	}
}

void Renderer::clipTriangleY(const IndexArray &originalVertices, IndexArray &clippedVertices, const ClipPlanes clipPlane, const float clipValue, RenderVertexArray &vertexArray)
{
	clippedVertices.clear();
	if (originalVertices.size() == 0) return;
	for (uint i = 0; i < originalVertices.size(); i++)
	{
		const uint index0 = originalVertices[i];
		const uint index1 = originalVertices[i == originalVertices.size() - 1 ? 0 : i + 1];

		const RenderVertex *v0 = vertexArray[index0];
		const RenderVertex *v1 = vertexArray[index1];

		if (v0->clipFlags & v1->clipFlags & clipPlane) // all outside
		{
		}
		else if (v0->clipFlags & clipPlane) // ouside to inside
		{
			clippedVertices.push_back(static_cast<uint32>(vertexArray.numVertices()));
			RenderVertex *v = vertexArray.insert();
			intersect(v, v0, v1, (clipValue - v0->screenspacePos.y) / (v1->screenspacePos.y - v0->screenspacePos.y));
			v->screenspacePos.y = clipValue;
		}
		else if (v1->clipFlags & clipPlane) // inside to outside
		{
			clippedVertices.push_back(index0);
			clippedVertices.push_back(static_cast<uint32>(vertexArray.numVertices()));
			RenderVertex *v = vertexArray.insert();
			intersect(v, v0, v1, (clipValue - v0->screenspacePos.y) / (v1->screenspacePos.y - v0->screenspacePos.y));
			v->screenspacePos.y = clipValue;
		}
		else
		{
			clippedVertices.push_back(index0);
		}
	}
}

void Renderer::clipTriangleZ(const IndexArray &originalVertices, IndexArray &clippedVertices, const ClipPlanes clipPlane, const float clipValue, RenderVertexArray &vertexArray)
{
	clippedVertices.clear();
	if (originalVertices.size() == 0) return;
	for (uint i = 0; i < originalVertices.size(); i++)
	{
		const uint index0 = originalVertices[i];
		const uint index1 = originalVertices[i == originalVertices.size() - 1 ? 0 : i + 1];
		
		const RenderVertex *v0 = vertexArray[index0];
		const RenderVertex *v1 = vertexArray[index1];

		if (v0->clipFlags & v1->clipFlags & clipPlane) // all outside
		{
		}
		else if (v0->clipFlags & clipPlane) // ouside to inside
		{
			clippedVertices.push_back(static_cast<uint32>(vertexArray.numVertices()));
			RenderVertex *v = vertexArray.insert();
			intersect(v, v0, v1, (clipValue - v0->viewspacePos.z) / (v1->viewspacePos.z - v0->viewspacePos.z));
			v->viewspacePos.z = clipValue;
			v->screenspacePos = project(m_projViewportMatrix, v->viewspacePos);
			v->clipFlags = computeClipFlags(v);
		}
		else if (v1->clipFlags & clipPlane) // inside to outside
		{
			clippedVertices.push_back(index0);
			clippedVertices.push_back(static_cast<uint32>(vertexArray.numVertices()));
			RenderVertex *v = vertexArray.insert();
			intersect(v, v0, v1, (clipValue - v0->viewspacePos.z) / (v1->viewspacePos.z - v0->viewspacePos.z));
			v->viewspacePos.z = clipValue;
			v->screenspacePos = project(m_projViewportMatrix, v->viewspacePos);
			v->clipFlags = computeClipFlags(v);
		}
		else
		{
			clippedVertices.push_back(index0);
		}
	}
}

void Renderer::clipTriangle(const IndexArray &originalVertices, IndexArray &clippedVertices, RenderVertexArray &vertexArray)
{
	IndexArray tmpVertices;

	clipTriangleZ(originalVertices, tmpVertices, PlaneNear, znear, vertexArray);

	clipTriangleZ(tmpVertices, clippedVertices, PlaneFar, zfar, vertexArray);

	clipTriangleX(clippedVertices, tmpVertices, PlaneLeft, m_clippingViewport.left, vertexArray);

	clipTriangleY(tmpVertices, clippedVertices, PlaneTop, m_clippingViewport.top, vertexArray);

	clipTriangleX(clippedVertices, tmpVertices, PlaneRight, m_clippingViewport.right, vertexArray);

	clipTriangleY(tmpVertices, clippedVertices, PlaneBottom, m_clippingViewport.bottom, vertexArray);
}

void Renderer::transform(const void *vertices, size_t numVertices, size_t firstVertex)
{
	ProfileBlock(transform);

	Matrix4x4 worldViewMatrix = m_worldMatrix * m_viewMatrix;
	Matrix4x4 worldViewMatrixNormalized = m_worldMatrix * m_viewMatrix; // todo: really normalize this matrix
	m_projViewportMatrix = m_projectionMatrix * m_viewportScaleMatrix;

	const uint8 *vertexStream = static_cast<const uint8 *>(vertices) + firstVertex * m_inputVertexFormat.vertexSize;

	m_transformedVertexArray.setNumVertices(numVertices);
	for (size_t i = 0; i < numVertices; i++, vertexStream += m_inputVertexFormat.vertexSize)
	{
		RenderVertex *transformedVertex = m_transformedVertexArray[i];
		// position always must be present
		const Vec3 *position = reinterpret_cast<const Vec3 *>(vertexStream + m_inputVertexFormat.positionOffset);
		transformedVertex->viewspacePos = worldViewMatrix * *position;
		transformedVertex->screenspacePos = project(m_projViewportMatrix, transformedVertex->viewspacePos);
		transformedVertex->clipFlags = computeClipFlags(transformedVertex);

		float *gradients = reinterpret_cast<float *>(reinterpret_cast<uint8 *>(transformedVertex) + sizeof(RenderVertex));
		if (m_inputVertexFormat.normalOffset >= 0)
		{
			const float *normal = reinterpret_cast<const float *>(vertexStream + m_inputVertexFormat.normalOffset);
			*gradients++ = normal[0];
			*gradients++ = normal[1];
			*gradients++ = normal[2];
		}
		if (m_inputVertexFormat.diffuseOffset >= 0)
		{
			const uint8 *diffuse = reinterpret_cast<const uint8 *>(vertexStream + m_inputVertexFormat.diffuseOffset);
			*gradients++ = diffuse[3] / 255.0f; // alpha
			*gradients++ = diffuse[2] / 255.0f;
			*gradients++ = diffuse[1] / 255.0f;
			*gradients++ = diffuse[0] / 255.0f;
		}
		if (m_inputVertexFormat.specularOffset >= 0)
		{
			const uint8 *specular = reinterpret_cast<const uint8 *>(vertexStream + m_inputVertexFormat.specularOffset);
			*gradients++ = specular[3] / 255.0f;
			*gradients++ = specular[2] / 255.0f;
			*gradients++ = specular[1] / 255.0f;
			*gradients++ = specular[0] / 255.0f;
		}
		const float *tex = reinterpret_cast<const float *>(vertexStream  + m_inputVertexFormat.textureOffset);
		for (uint j = 0; j < m_inputVertexFormat.numTextureCoords * 2; j++)
		{
			*gradients++ = tex[j];
		}
	}
}

void Renderer::clip(size_t i0, size_t i1, size_t i2)
{
	RenderVertex *v0 = m_transformedVertexArray[i0];
	RenderVertex *v1 = m_transformedVertexArray[i1];
	RenderVertex *v2 = m_transformedVertexArray[i2];

	if (v0->clipFlags & v1->clipFlags & v2->clipFlags)
	{
		// triangle is fully out
		return;
	}
	if ((v2->screenspacePos.x - v0->screenspacePos.x) * (v1->screenspacePos.y - v0->screenspacePos.y) <
		(v2->screenspacePos.y - v0->screenspacePos.y) * (v1->screenspacePos.x - v0->screenspacePos.x))
	{
		// triangle is backfacing
		//continue;
	}

	uint clipFlags = v0->clipFlags | v1->clipFlags | v2->clipFlags;
	if (clipFlags == 0)
	{
		// triangle is fully inside
		m_transformedIndexArray.push_back(i0);
		m_transformedIndexArray.push_back(i1);
		m_transformedIndexArray.push_back(i2);
	}
	else
	{
		// triangle is clipped
		uint32 triangleIndices[3] = { i0, i1, i2 };
		const IndexArray originalVertices(triangleIndices, triangleIndices + 3);
		IndexArray clippedVertices;

		clipTriangle(originalVertices, clippedVertices, m_transformedVertexArray);
		for (size_t i = 1; i + 1 < clippedVertices.size(); i++)
		{
			m_transformedIndexArray.push_back(clippedVertices[0]);
			m_transformedIndexArray.push_back(clippedVertices[i + 0]);
			m_transformedIndexArray.push_back(clippedVertices[i + 1]);
		}
	}
}

void Renderer::draw(Primitive::Type primitiveType, uint numPrimitives, uint firstVertex)
{
	sweglAssert(primitiveType == Primitive::TRIANGLE_LIST || primitiveType == Primitive::TRIANGLE_STRIP); // currently linited support
	sweglAssert(m_vertexBuffer != 0); // todo: check vb size

	const void *vertices = m_vertexBuffer->data();
	size_t numVertices = 0;
	if (primitiveType == Primitive::TRIANGLE_LIST)
		numVertices = numPrimitives * 3;
	else if (primitiveType == Primitive::TRIANGLE_STRIP)
		numVertices = numPrimitives + 2;

	transform(vertices, numVertices, firstVertex);

	if (primitiveType == Primitive::TRIANGLE_LIST)
	{
		ProfileBlock(clipping);
		m_transformedIndexArray.resize(0);
		for (uint i = 0, end = numVertices; i < end; i += 3)
		{
			clip(i, i + 1, i + 2);
		}
	}
	else if (primitiveType == Primitive::TRIANGLE_STRIP)
	{
		ProfileBlock(clipping);
		m_transformedIndexArray.resize(0);
		for (uint i = 0, end = numPrimitives; i < end; i++)
		{
			clip(i, i + 1, i + 2);
		}
	}

	{
		ProfileBlock(polyfiller_new);
		for (size_t i = 0, n = m_transformedIndexArray.size(); i < n; i += 3)
		{
			m_rasterizer.drawTriangle(m_transformedVertexArray[m_transformedIndexArray[i + 0]], m_transformedVertexArray[m_transformedIndexArray[i + 1]], m_transformedVertexArray[m_transformedIndexArray[i + 2]]);
		}
	}
}

void Renderer::drawIndexed(Primitive::Type primitiveType, uint numPrimitives, uint startIndex)
{
	sweglAssert(primitiveType == Primitive::TRIANGLE_LIST); // currently linited support
	sweglAssert(m_vertexBuffer != 0); // todo: check vb size
	sweglAssert(m_indexBuffer != 0); // todo: check ib size

	const uint16 *indices = m_indexBuffer->data<uint16>(); // todo: support 32 bit indices
	const void *vertices = m_vertexBuffer->data();
	size_t numVertices = m_vertexBuffer->size() / m_inputVertexFormat.vertexSize; // todo: change this, search the maximum indices in the index buffer!

	transform(vertices, numVertices, 0);

	{
		ProfileBlock(clipping);
		m_transformedIndexArray.resize(0);
		for (uint i = startIndex, end = startIndex + 3 * numPrimitives; i < end; i += 3, indices += 3)
		{
			clip(indices[0], indices[1], indices[2]);
		}
	}

	{
	ProfileBlock(polyfiller_new);

	for (size_t i = 0, n = m_transformedIndexArray.size(); i < n; i += 3)
	{
		m_rasterizer.drawTriangle(m_transformedVertexArray[m_transformedIndexArray[i + 0]], m_transformedVertexArray[m_transformedIndexArray[i + 1]], m_transformedVertexArray[m_transformedIndexArray[i + 2]]);
	}
	}
}

void Renderer::present()
{
}

Rasterizer::Rasterizer()
{
}

Rasterizer::~Rasterizer()
{
}

bool TriangleInfo::initialize(const RenderVertex *v0, const RenderVertex *v1, const RenderVertex *v2, Rasterizer &rasterizer)
{
	const float fdxy[4] = { v1->screenspacePos.x - v0->screenspacePos.x, v2->screenspacePos.x - v0->screenspacePos.x, v1->screenspacePos.y - v0->screenspacePos.y, v2->screenspacePos.y - v0->screenspacePos.y };

	const float ratio = fdxy[0] * fdxy[3] - fdxy[1] * fdxy[2];
	if (ratio == 0.0f)
		return false;

	const float gradient = 1.0f / ratio; // will be used for z correction

	z.x = ((v1->screenspacePos.z - v0->screenspacePos.z) * fdxy[3] - (v2->screenspacePos.z - v0->screenspacePos.z) * fdxy[2]) * gradient;
	z.y = ((v2->screenspacePos.z - v0->screenspacePos.z) * fdxy[0] - (v1->screenspacePos.z - v0->screenspacePos.z) * fdxy[1]) * gradient;
	z.reference = v0->screenspacePos.z;

	const float *v0GradientRefs = reinterpret_cast<const float *>(reinterpret_cast<const uint8 *>(v0) + sizeof(RenderVertex));
	const float *v1GradientRefs = reinterpret_cast<const float *>(reinterpret_cast<const uint8 *>(v1) + sizeof(RenderVertex));
	const float *v2GradientRefs = reinterpret_cast<const float *>(reinterpret_cast<const uint8 *>(v2) + sizeof(RenderVertex));
	for (size_t i = 0; i < rasterizer.vertexFormat.numGradients; i++)
	{
		gradients[i].x = ((v1GradientRefs[i] - v0GradientRefs[i]) * fdxy[3] - (v2GradientRefs[i] - v0GradientRefs[i]) * fdxy[2]) * gradient;
		gradients[i].y = ((v2GradientRefs[i] - v0GradientRefs[i]) * fdxy[0] - (v1GradientRefs[i] - v0GradientRefs[i]) * fdxy[1]) * gradient;
		gradients[i].reference = v0GradientRefs[i];
	}

	m_fRefX	= v0->screenspacePos.x;
	m_fRefY = v0->screenspacePos.y;

	// sort vertices by y-coordinate
	const RenderVertex *vertices[3] = { v0, v1, v2 };
	if (v1->screenspacePos.y < vertices[0]->screenspacePos.y) 
	{ 
		vertices[1] = vertices[0]; 
		vertices[0] = v1; 
	}
	if (v2->screenspacePos.y < vertices[0]->screenspacePos.y) 
	{
		vertices[2] = vertices[1]; 
		vertices[1] = vertices[0]; 
		vertices[0] = v2; 
	}
	else if (v2->screenspacePos.y < vertices[1]->screenspacePos.y) 
	{ 
		vertices[2] = vertices[1]; 
		vertices[1] = v2; 
	}

	// save triangle coordinates
	m_fAx = vertices[0]->screenspacePos.x;
	m_fAy = vertices[0]->screenspacePos.y;
	m_fBx = vertices[1]->screenspacePos.x;
	m_fBy = vertices[1]->screenspacePos.y;	
	m_fCx = vertices[2]->screenspacePos.x;
	m_fCy = vertices[2]->screenspacePos.y;
#if 0
	const float texelArea = (v1->u - v0->u) * (v2->v - v0->v) - (v2->u - v0->u) * (v1->v - v0->v); // area = cross product			
	float fMinRatio = Abs(texelArea * gradient);
	const float textureArea = 256 * 256; // hardcoded!
	fMinRatio *= textureArea;
	fMinRatio = Sqrt(fMinRatio);	
	//if (fMinRatio > m_fMaxMinRatio )
	//	fMinRatio = m_fMaxMinRatio;
	mipLevel = 0;
	if( fMinRatio > 1.0f )
	{
		mipLevel = static_cast<uint>(log(fMinRatio)); // should be log2
		if (mipLevel > 4) mipLevel = 4;
	}
#endif
	return true;
}

static inline uint32 blend(uint32 c1, uint32 c2)
{
	int r = (((c1 & 0x00ff0000) + (c2 & 0x00ff0000)) / 2) & 0x00ff0000;
	int g = (((c1 & 0x0000ff00) + (c2 & 0x0000ff00)) / 2) & 0x0000ff00;
	int b = (((c1 & 0x000000ff) + (c2 & 0x000000ff)) / 2) & 0x000000ff;
	return r | g | b;
}

static inline uint32 blend2(uint32 c1, uint32 c2)
{
	int r1 = (c1 & 0x00ff0000) >> 16;
	int g1 = (c1 & 0x0000ff00) >> 8;
	int b1 = (c1 & 0x000000ff) >> 0;
	int r2 = (c2 & 0x00ff0000) >> 16;
	int g2 = (c2 & 0x0000ff00) >> 8;
	int b2 = (c2 & 0x000000ff) >> 0;
	int r = (r1 * r2) / 256;
	int g = (g1 * g2) / 256;
	int b = (b1 * b2) / 256;
	return (r << 16) | (g << 8) | b;
}

static inline uint32 blend3(uint32 c1, uint32 c2)
{
	float r1 = static_cast<float>((c1 & 0x00ff0000) >> 16) / 255;
	float g1 = static_cast<float>((c1 & 0x0000ff00) >> 8) / 255;
	float b1 = static_cast<float>((c1 & 0x000000ff) >> 0) / 255;
	float r2 = static_cast<float>((c2 & 0x00ff0000) >> 16) / 255;
	float g2 = static_cast<float>((c2 & 0x0000ff00) >> 8) / 255;
	float b2 = static_cast<float>((c2 & 0x000000ff) >> 0) / 255;
	float r = (r1 * r2);
	float g = (g1 * g2);
	float b = (b1 * b2);
	return (static_cast<int>(r * 255) << 16) | (static_cast<int>(g * 255) << 8) | static_cast<int>(b * 255);
}

static inline Color modulate(Color c1, Color c2)
{
	return Color(1.0f, c1.r * c2.r, c1.g * c2.g, c1.b * c2.b);
}

static inline Color modulate2X(Color c1, Color c2)
{
	// todo: clean this and user SSE min instruction
	return Color(1.0f, c1.r * c2.r * 2, c1.g * c2.g * 2, c1.b * c2.b * 2).clamp();
}

static inline Color modulate4X(Color c1, Color c2)
{
	// todo: clean this and user SSE min instruction
	return Color(1.0f, c1.r * c2.r * 4, c1.g * c2.g * 4, c1.b * c2.b * 4).clamp();
}

static inline Color add(Color c1, Color c2)
{
	return Color(1.0f, c1.r + c2.r, c1.g + c2.g, c1.b + c2.b).clamp();
}

static inline Color addSigned(Color c1, Color c2)
{
	return Color(1.0f, c1.r + c2.r - 0.5f, c1.g + c2.g - 0.5f, c1.b + c2.b - 0.5f).clamp();
}

static inline Color addSigned2X(Color c1, Color c2)
{
	return Color(1.0f, (c1.r + c2.r - 0.5f) * 2.0f, (c1.g + c2.g - 0.5f) * 2.0f, (c1.b + c2.b - 0.5f) * 2.0f).clamp();
}

static inline Color substract(Color c1, Color c2)
{
	return Color(1.0f, c1.r - c2.r, c1.g - c2.g, c1.b - c2.b).clamp();
}

static inline Color addSmooth(Color c1, Color c2)
{
	return Color(1.0f, c1.r + c2.r * (1.0f - c1.r), c1.g + c2.g * (1.0f - c1.g), c1.b + c2.b * (1.0f - c1.b)).clamp();
}

void Rasterizer::drawTriangle(const RenderVertex *v0, const RenderVertex *v1, const RenderVertex *v2)
{
	TriangleInfo triangle;

	// initialize scanline
	if (! triangle.initialize(v0, v1, v2, *this))
		return;

	// Calculate slopes for stepping
	const float fslope0 = (triangle.m_fBy > triangle.m_fAy) ? (triangle.m_fBx - triangle.m_fAx) / (triangle.m_fBy - triangle.m_fAy) : 0.0f;
	const float fslope1 = (triangle.m_fCy > triangle.m_fAy) ? (triangle.m_fCx - triangle.m_fAx) / (triangle.m_fCy - triangle.m_fAy) : 0.0f;
	const float fslope2 = (triangle.m_fCy > triangle.m_fBy) ? (triangle.m_fCx - triangle.m_fBx) / (triangle.m_fCy - triangle.m_fBy) : 0.0f;

	uint32 *frameBuffer;
	float *depthBuffer;
	float fendX, fstartX;
	sint icurrentY;
	for (int i = 0; i < 2; i++)
	{
		float fslope[2];
		sint iendY; // y end

		if (i == 0)
		{
			// upper part
			iendY = Ceil<sint>(triangle.m_fBy);
			if (fslope0 > fslope1)
			{
				fslope[0] = fslope1;
				fslope[1] = fslope0;
			}
			else
			{
				fslope[0] = fslope0;
				fslope[1] = fslope1;
			}
			icurrentY = Ceil<sint>(triangle.m_fAy);

			frameBuffer = m_colorBuffer->data<uint32>() + icurrentY * m_colorBuffer->width();
			depthBuffer = m_depthBuffer->data<float>() + icurrentY * m_colorBuffer->width();

			const float fstartY = static_cast<float>(icurrentY);
			const float foffset = fstartY - triangle.m_fAy; // subpixel adjustment
			fstartX = triangle.m_fAx + fslope[0] * foffset;
			fendX = triangle.m_fAx + fslope[1] * foffset;

			const float fOffsetX = fstartX - triangle.m_fRefX;
			const float fOffsetY = fstartY - triangle.m_fRefY;

			triangle.z.z = triangle.z.reference + triangle.z.x * fOffsetX + triangle.z.y * fOffsetY;
			triangle.z.w = triangle.z.x * fslope[0] + triangle.z.y;
			for (size_t i = 0; i < vertexFormat.numGradients; i++)
			{
				triangle.gradients[i].z = triangle.gradients[i].reference + triangle.gradients[i].x * fOffsetX + triangle.gradients[i].y * fOffsetY;
				triangle.gradients[i].w = triangle.gradients[i].x * fslope[0] + triangle.gradients[i].y;
			}
		}
		else
		{
			// lower part
			iendY = Ceil<sint>(triangle.m_fCy);
			const float fstartY = static_cast<float>(icurrentY);
			const float foffset = fstartY - triangle.m_fBy;
			if (fslope1 > fslope2)
			{
				fslope[0] = fslope1;
				fslope[1] = fslope2;
				fendX = triangle.m_fBx + fslope[1] * foffset;

				triangle.z.w = triangle.z.x * fslope[0] + triangle.z.y;
				for (size_t i = 0; i < vertexFormat.numGradients; i++)
				{
					triangle.gradients[i].w = triangle.gradients[i].x * fslope[0] + triangle.gradients[i].y;
				}
			}
			else
			{
				fslope[0] = fslope2;
				fslope[1] = fslope1;
				fstartX = triangle.m_fBx + fslope[0] * foffset;

				const float fOffsetX = fstartX - triangle.m_fRefX;
				const float fOffsetY = fstartY - triangle.m_fRefY;	

				triangle.z.z = triangle.z.reference + triangle.z.x * fOffsetX + triangle.z.y * fOffsetY;
				triangle.z.w = triangle.z.x * fslope[0] + triangle.z.y;
				for (size_t i = 0; i < vertexFormat.numGradients; i++)
				{
					triangle.gradients[i].z = triangle.gradients[i].reference + triangle.gradients[i].x * fOffsetX + triangle.gradients[i].y * fOffsetY;
					triangle.gradients[i].w = triangle.gradients[i].x * fslope[0] + triangle.gradients[i].y;
				}
			}
		}

		for ( ; icurrentY < iendY; icurrentY++, fstartX += fslope[0], fendX += fslope[1])
		{
			sint icurrentX = Ceil<sint>(fstartX);
			sint iendX = Ceil<sint>(fendX);

			float fz = triangle.z.z;
			triangle.z.z += triangle.z.w;

			float fgradientValues[4 + 4 + 2 * 8];
			for (size_t i = 0; i < vertexFormat.numGradients; i++)
			{
				fgradientValues[i] = triangle.gradients[i].z;
				triangle.gradients[i].z += triangle.gradients[i].w;
			}

			uint32 *pixel = frameBuffer + icurrentX;
			float *depth = depthBuffer + icurrentX;
			// horizonal scanline
			while (icurrentX < iendX)
			{
				// gab: on fait du pixel shader ?
				if (*depth > fz)
				{
					Color diffuse(1, 1, 1, 1); // default color, white
					if (vertexFormat.diffuseOffset >= 0)
					{
						diffuse = Color(fgradientValues[vertexFormat.diffuseOffset], fgradientValues[vertexFormat.diffuseOffset + 1], fgradientValues[vertexFormat.diffuseOffset + 2], fgradientValues[vertexFormat.diffuseOffset + 3]);
					}
					Color current(0), arg1(0), arg2(0); // todo: check if all default values are really needeed
					current = diffuse;
					for (int i = 0; i < 8 && m_textureStages[i].m_colorOp != TextureStage::Disabled; i++)
					{
						switch (m_textureStages[i].m_colorArg1)
						{
						case TextureStage::Texture:
							{
								int texCoordIndex = m_textureStages[i].texCoordIndex;
								arg1 = m_textureUnits[i].sample(fgradientValues[vertexFormat.textureOffset + texCoordIndex * 2], fgradientValues[vertexFormat.textureOffset + texCoordIndex * 2 + 1]);
								break;
							}
						case TextureStage::Diffuse: arg1 = diffuse; break;
						case TextureStage::Specular: arg1 = diffuse; break;
						case TextureStage::Constant: arg1 = Color(0); break;
						case TextureStage::TextureFactor: sweglAssert(0); break;
						case TextureStage::Temp: sweglAssert(0); break;
						case TextureStage::Current: arg1 = current; break;
						default: sweglAssert(0);
						}

						switch (m_textureStages[i].m_colorArg2)
						{
						case TextureStage::Texture:
							{
								int texCoordIndex = m_textureStages[i].texCoordIndex;
								arg2 = m_textureUnits[i].sample(fgradientValues[vertexFormat.textureOffset + texCoordIndex * 2], fgradientValues[vertexFormat.textureOffset + texCoordIndex * 2 + 1]);
								break;
							}
						case TextureStage::Diffuse: arg2 = diffuse; break;
						case TextureStage::Specular: arg2 = diffuse; break;
						case TextureStage::Constant: arg2 = Color(0); break;
						case TextureStage::TextureFactor: sweglAssert(0); break;
						case TextureStage::Temp: sweglAssert(0); break;
						case TextureStage::Current: arg2 = current; break;
						default: sweglAssert(0);
						}

						switch (m_textureStages[i].m_colorOp)
						{
						case TextureStage::Modulate:
							current = modulate(arg1, arg2);
							break;
						case TextureStage::Modulate2X:
							current = modulate2X(arg1, arg2);
							break;
						case TextureStage::Modulate4X:
							current = modulate4X(arg1, arg2);
							break;
						case TextureStage::Add:
							current = add(arg1, arg2);
							break;
						case TextureStage::AddSigned:
							current = addSigned(arg1, arg2);
							break;
						case TextureStage::AddSigned2X:
							current = addSigned2X(arg1, arg2);
							break;
						case TextureStage::Substract:
							current = substract(arg1, arg2);
							break;
						case TextureStage::AddSmooth:
							current = addSmooth(arg1, arg2);
							break;
						default:
							current = diffuse;
						}
					}
					*pixel = current.asUint32();
					*depth = fz;
				}					
				// move to next pixel...
				fz += triangle.z.x;
				for (size_t i = 0; i < vertexFormat.numGradients; i++)
				{
					fgradientValues[i] += triangle.gradients[i].x;
				}
				icurrentX++;
				pixel++;
				depth++;
			}
			// move to next scanline...
			frameBuffer += m_colorBuffer->width();
			depthBuffer += m_colorBuffer->width();
		}
	}
}

}

